Express


Posted by ericcch24 on 2020-10-16

Express get started:route 路由部分

basic routing: 表示 '/' 的 request 要給誰處理,導去哪裡

const express = require('express')
const app = express()
const port = 5001

app.get('/', (req, res) => { 
// basic routing: 表示 '/' 的 request 要給誰處理,導去哪裡
  res.send('Hello World!')
})

app.get('/poop', (req, res) => { // 表示 '/poop' 的 request 要給誰處理,導去哪裡
  res.send('Hello Poop!')
})


app.listen(port, () => {
  console.log(`Example app listening at http://localhost:5001`)
})

Express 與 Apache + PHP 的差異

  • Apache + PHP: 會侷限在檔案系統,檔案結構長怎樣,url 就長怎樣。
  • Express: 不需經過額外 php 處理(==待查證==),自己本身就是 server,透過路由系統(basic routing)可以根據不同的 url 回傳不同的結果,不需要有 js 檔案之類。

Express 基本架構與 MVC

  • Model: 負責管理資料
  • View: 顯示相關的模板 template
  • Controller: 中間的協調者

Express 的 view (template engine) 寫法

  • 先建立一個檔名是 view 的資料夾,裡面放 ejs 檔案
    ```javascript=
    .
    .
    .
    app.set('view engine', 'ejs')

// 第一個參數 /hello 為網址列路徑
app.get('/hello', (req, res) => {
res.render('hello')
// render view 底下的 hello 檔案
})
.
.
.

---
* 簡單範例

在 index.js 建立一個 todo 的陣列
```javascript=
.
.
const todos = ['first todo', 'second todo', 'third todo']
app.get('/todos', (req, res) => { 
  res.render('todos', { // 第二個參數把資料傳進去
    todos: todos
  })
})
.
.

建立 todo.ejs 的 view

<h1>todos</h1>

<ul>
  <% for(let i = 0; i < todos.length; i++) { %>
    <li><%= todos[i] %></li> <!-- 用 = 輸出資料 --> 
  <% } %>
</ul>
  • ==template engine 會自己做 escape,所以 <%= 輸出內容 %>可以避免 XSS==,用 <%- 輸出內容 %>就不會 escape

  • 拿取網址列的參數 :id
    .
    .
    const todos = ['first todo', 'second todo', 'third todo']
    app.get('/todos/:id', (req,res) => {
    const id  = req.params.id // 可以拿到上面網址列的 id
    const todo = todos[id]
    res.render('todo', {
      todo
    })
    })
    .
    .
    

Express 的 model 與 controller 重構寫法

  • model 部分負責處理資料,把要用的資料包成 function
    ```javascript=
    const todos = ['first todo', 'second todo', 'third todo']

const todoModel = {
getAll: () => {
return todos
},

get: id => {
return todos[id]
}
}

module.exports = todoModel

* controller 拿 model 的資料,送給 view 去 render
```javascript=
const todoModel = require('../models/todo')


const todoController = {
  getAll: (req, res) => {
    const todos = todoModel.getAll()
    res.render('todos', {
      todos: todos
    })
  },
  get: (req, res) => {
    const id = req.params.id
    const todo = todoModel.get(id)
    res.render('todo', {
      todo: todo
    })
  }
}


module.exports = todoController
  • 而一開始的路由 index 就可以直接導到 controller,之前上半部混著寫的部分就可以改成下半部直接用 function
app.get('/todos', (req, res) => { 
  res.render('todos', { // 第二個參數把資料傳進去
    todos: todos
  })
})

app.get('/todos/:id', (req,res) => {
  const id  = req.params.id // 可以拿到上面網址列的 id
  const todo = todos[id]
  res.render('todo', {
    todo
  })
})

===

app.get('/todos', todoController.getAll)

app.get('/todos/:id', todoController.get)

Node.js 與 MySQL 的串接

串接時遇到的問題

Q: 問一個有關 BE201 Node.js 與 MySQL 的串接影片實作的相關問題,我照著影片做之後,噴出第二張圖的錯誤 Error: connect ECONNREFUSED 127.0.0.1:3306,查了一陣子發現是 port 預設不一樣,我就把 port 改成之前第九週在用 XAMPP 時的 localhost:8080 (第一張圖),再送出之後 terminal 跑了一陣子又噴第三張圖的錯誤 Error: Connection lost: The server closed the connection.,查了很久大概都是說可能資料庫那邊沒弄好,但是我確定我 XAMPP 有開好,phpmyadmin 那邊也確定可以開也看得到裡面資料,找超久還是不知道問題出在哪裡。

A: 因為 xampp 是虛擬機,所以你 localhost:3306 本來就是沒有東西的,那為什麼 localhost:8080 會有東西呢?是因為在 xampp 的設定裡面,有把虛擬機的 8080 port 對應到你 localhost 的 8080 port,所以你 localhost:8080 就是連到虛擬機的 8080 port。因此解法就是把虛擬機的 3306 port 對應到自己的 3306 port,建立一個通道。
意思就是如果用虛擬機的 mysql,那你就要把虛擬機的 3306 對應到自己的 3306,連 localhost:3306 就等於是連 remote:3306

簡單來說要把虛擬機想成是另外一台主機比較好,而不是在你電腦裡的東西。

延伸資料:
關於 SSH Tunnel 連線
通訊埠轉發 Port Forwarding 設定教學

todo 範例

  1. 建立 mysql 連線檔
    參考資料:github mysql.js
    ```javascript=
    // db.js
    var mysql = require('mysql');
    var connection = mysql.createConnection({
    host : 'localhost',
    user : 'ericcch24',
    password : 'zxc124040219',
    database : 'ericcch24'
    });

module.exports = connection

2. index.js 設定連線
```javascript=
const express = require('express')
const app = express()
const port = 5001
const todoController = require('./controllers/todo')
const db = require('./db')


app.set('view engine', 'ejs')

// 設定網址列路徑
app.get('/todos', todoController.getAll)
app.get('/todos/:id', todoController.get)

app.listen(port, () => {
  db.connect(); // 設定連線到資料庫
  console.log(`Example app listening at http://localhost:${port}`);
})
  1. 設定 model
    ```javascript=
    // /models/todo.js
    const db = require('../db')

const todoModel = {
getAll: (cb) => { // 要用 callback 才可以拿資料
db.query('SELECT * from todos', (err, results) => {
if (err) return cb(err);
cb(null, results);
// 這裡設定第一個參數是 err, 第二個是 result
// 後面的 controller 就可以拿這邊的 callback:
// todoModel.getAll((err, result) => {})
});
},

get: (id, cb) => {
db.query('SELECT * from todos where id = ?', [id],
// 預防 SQL injection 所以用 prepare statement 語法
// 第二個參數在陣列內部放要取代問號的東西
(err, results) => {
if (err) return cb(err);
cb(null, results);
});
}
}

module.exports = todoModel

類似 MySQL 的 prepared statements 作法,==預防 SQL injection==
```javascript=
connection.query('UPDATE users SET foo = ?, bar = ?, baz = ? WHERE id = ?', ['a', 'b', 'c', userId], function (error, results, fields) {
  if (error) throw error;
  // ...
});
  1. 設定 controller
    ```javascript=
    // /controllers/todo.js
    const todoModel = require('../models/todo')

const todoController = {
getAll: (req, res) => {
todoModel.getAll((err, result) => {
if(err) return console.log(err);
res.render('todos', {
todos: result
})
})

},
get: (req, res) => {
const id = req.params.id
todoModel.get(id, (err,result) => {
res.render('todo', {
todo: result[0]
})
})
}
}

module.exports = todoController

result 長這樣:
```javascript=
[
  RowDataPacket { id: 1, content: 'todo1' },
  RowDataPacket { id: 2, content: 'todo2' },
  RowDataPacket { id: 3, content: 'todo3' }
]
  1. 設定 view
    ```javascript=

    todos

    <% for(let i = 0; i < todos.length; i++) { %>
  • <%= todos[i].id + ': ' + todos[i].content %>
  • <% } %>
```javascript=
<h1>todo</h1>


<h2><%= todo.content %></h2>
// 因為拿到的資料是物件
tags: Week17

#week17







Related Posts

使用 TensorFlow 來做簡單的手寫數字辨識

使用 TensorFlow 來做簡單的手寫數字辨識

金魚系列、 RWD (上)

金魚系列、 RWD (上)

1731. The Number of Employees Which Report to Each Employee

1731. The Number of Employees Which Report to Each Employee


Comments